﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace EislaufScb.Model.Repository
{
    public class BaseRepository : IPersistableRepository, IDisposable
    {
        protected ClubModelContainer context;

        /// <summary>
        /// Is set to true if in the save method no exception is thrown.
        /// </summary>
        private bool disposable = false;

        public BaseRepository(ClubModelContainer context)
        {
            this.context = context;
        }

        /// <summary>
        /// Gets a single object of a given type.
        /// </summary>
        /// <typeparam name="T">Could be BaseEntitySet.OfType BaseEntitySet_Blog</typeparam>
        /// <param name="entity">The entity to load.</param>
        /// <returns>The loaded entity given by T</returns>
        public T GetObject<T>(T entity) where T : BaseEntity
        {
            return context.BaseEntitySet.OfType<T>().Where(e => e.Id.Equals(entity.Id)).First();
        }

        /// <summary>
        /// Get's a list of given entities for T.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns>List T of the given parameter type </></returns>
        public List<T> GetAll<T>() where T : BaseEntity
        {
            return context.BaseEntitySet.OfType<T>().ToList();
        }

        /// <summary>
        /// Loads an entry by id (identifier in the database).
        /// </summary>
        /// <typeparam name="T">Type of the entity to load.</typeparam>
        /// <param name="id">The id for the entity to look up.</param>
        /// <returns>NULL if nothing is found otherwise the appropriate entity.</returns>
        public T GetEntryById<T>(long id) where T : BaseEntity
        {
            var result = context.BaseEntitySet.OfType<T>().Where(entry => entry.Id == id).FirstOrDefault();
            return result;
        }

        /// <summary>
        /// Removes an entry by a given id.
        /// </summary>
        /// <typeparam name="T">The entity type to remove.</typeparam>
        /// <param name="id">The id of the entity that will be removed.</param>
        public void RemoveEntryById<T>(long id) where T : BaseEntity
        {
            var persistenceObject = GetEntryById<T>(id);
            context.BaseEntitySet.DeleteObject(persistenceObject);
        }

        /// <summary>
        /// Updates an entity with passed value in the
        /// parameter (updated entity from outside).
        /// </summary>
        /// <typeparam name="T">The type of entity to update</typeparam>
        /// <param name="persistenceObject">The object that will be updated.</param>
        public void UpdateEntry<T>(T persistenceObject) where T : BaseEntity
        {
            context.ObjectStateManager.ChangeObjectState(persistenceObject, EntityState.Modified);
        }

        /// <summary>
        /// Adds a new object to the entity context.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="persistenceObject">The newly created entity from the caller.</param>
        public void AddEntry<T>(T persistenceObject) where T : BaseEntity
        {
            context.BaseEntitySet.AddObject(persistenceObject);
        }

        /// <summary>
        /// Saving the context.
        /// </summary>
        public void Save(BaseEntity baseEntity)
        {
            try
            {
                if (context == null)
                {
                    throw new Exception("The current transaction is not started");
                }

                context.SaveChanges();
            }
            catch (OptimisticConcurrencyException optimisticException)
            {
                Console.WriteLine(optimisticException.Message);
                context.Refresh(System.Data.Objects.RefreshMode.StoreWins, baseEntity);                
            }
            finally
            {                
                disposable = true;
            }
        }

        public void Dispose()
        {
            if (disposable)
            {
                context.Dispose();
            }
        }
    }
}
